#ifndef _COM_FUNC_HPP_
#define _COM_FUNC_HPP_

#include <dlcom/unknown.h>

#define MAX_PROGIDLEN    100
#define MAX_CONTAINERLEN 100
#define MAX_PLUGIN       10

#define DllGetClassObjectFuncName   "DllGetClassObject"
#define DllGetAtFuncName            "DllGetAt"
#define DllGetCountFuncName         "DllGetCount"
#define DllProgIDFromCLSIDFuncName  "DllProgIDFromCLSID"
#define DllCanUnloadNowFuncName     "DllCanUnloadNow"
#define DllRegisterServerFuncName   "DllRegisterServer"
#define DllUnregisterServerFuncName "DllUnregisterServer"
#define DllStartFuncName            "DllStart"
#define DllStopFuncName             "DllStop"
#define DllSetupFuncName            "DllSetup"
#define DllGetVersionFuncName       "DllGetVersion"
#define NULLFuncName                "null"

typedef HRESULT(OS_STDCALL* DefineDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
typedef CLSID(OS_STDCALL* DefineDllGetAt)(LONG nIndex);
typedef LONG(OS_STDCALL* DefineDllGetCount)();
typedef LPCSTR(OS_STDCALL* DefineDllProgIDFromCLSID)(REFCLSID clsid);
typedef HRESULT(OS_STDCALL* DefineDllCanUnloadNow)(void);
typedef HRESULT(OS_STDCALL* DefineDllRegisterServer)(void);
typedef HRESULT(OS_STDCALL* DefineDllUnregisterServer)(void);
typedef HRESULT(OS_STDCALL* DefineDllStart)(void);
typedef HRESULT(OS_STDCALL* DefineDllStop)(void);
typedef HRESULT(OS_STDCALL* DefineDllSetup)(UINT type, UINT version);
typedef LONG(OS_STDCALL* DefineDllGetVersion)(void);

#define BEGIN_STDCOM_MAP \
    std_method(QueryInterface)(REFIID riid, void** ppv) \
    {
#define STDCOM_INTERFACE_ENTRY_UNKNOWN \
    if (IID_IBase == riid) { \
        *ppv = static_cast<IBase*>(this); \
        this->AddRef(); \
        return S_OK; \
    }

#define STDCOM_INTERFACE_ENTRY_UNKNOWN_(icast) \
    if (IID_IBase == riid) { \
        *ppv = static_cast<IBase*>(static_cast<icast*>(this)); \
        this->AddRef(); \
        return S_OK; \
    }

#define STDCOM_INTERFACE_ENTRY(iface) \
    if (_uuidof(iface) == riid) { \
        *ppv = static_cast<iface*>(this); \
        this->AddRef(); \
        return S_OK; \
    }

#define STDCOM_INTERFACE_BASECLASS_ENTRY_(icast, iface) \
    if (_uuidof(iface) == riid) { \
        *ppv = static_cast<iface*>(static_cast<icast*>(this)); \
        this->AddRef(); \
        return S_OK; \
    }

#define STDCOM_INTERFACE_MEMBER_ENTRY(icast, iface) \
    if (_uuidof(iface) == riid) { \
        return this->icast->QueryInterface(riid, ppv); \
    }

#define STDCOM_INTERFACE_MEMBER_ENTRY_(icast, iid, iface) \
    if (iface == riid) { \
        return this->icast->QueryInterface(_uuidof(iid), ppv); \
    }

#define STDCOM_ADDREF \
    std_method_(ULONG, AddRef)() \
    { \
        _atomic_add(&this->ref); \
        return this->ref.value; \
    }

#define STDCOM_RELEASE \
    std_method_(ULONG, Release)() \
    { \
        _atomic_del(&this->ref); \
        if (this->ref.value) { \
            return this->ref.value; \
        } else { \
            delete this; \
        } \
        return 0; \
    }

#define END_STDCOM_MAP \
    return E_NOINTERFACE; \
    } \
    STDCOM_ADDREF STDCOM_RELEASE

#define BEGIN_STDCOM_MAP_EX \
    std_method(QueryInterface)(REFIID riid, void** ppv) \
    {
#define END_STDCOM_MAP_EX \
    return E_NOINTERFACE; \
    }

#endif
